package com.sparkseries.route;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 网关动态路由加载
 * TODO 考虑是否实现基于Redis动态更新
 */
@Slf4j
@RefreshScope
@Component
public class DynamicRouteLoader  implements ApplicationEventPublisherAware {
    @Resource
    private NacosDiscoveryProperties nacosProperties;
    @Resource
    private DynamicRouteService dynamicRouteService;
    private ApplicationEventPublisher publisher;
    private ConfigService configService;
    private static final String DATA_ID = "gateway.json";
    public static final long DEFAULT_TIMEOUT = 30000L;


    private void informChanged() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * 初始化路由
     */
    public void init() {
        loadRoute();
    }

    /**
     * 刷新路由
     */
    public Mono<String> refresh() {
        this.init();
        return Mono.empty();
    }

    /**
     * 从 Nacos 中读取路由配置
     */
    private void loadRoute() {
        List<RouteDefinition> routes = new ArrayList<>();
        configService = initConfigService();
        if (Objects.isNull(configService)) {
            log.warn("InitConfigService Fail");
        }
        try {
            String config = configService.getConfig(DATA_ID, nacosProperties.getGroup(), DEFAULT_TIMEOUT);
            if (StringUtils.isNotBlank(config)) {
                routes = JSON.parseArray(config, RouteDefinition.class);
            }else {
                log.warn("Nacos中未获取到网关配置，请确认Nacos是否正确配置");
            }
        } catch (NacosException e) {
            log.error("初始化网关路由错误", e);
            // TODO 日志功能实现后 将异常打印到日志中
            e.printStackTrace();
        }
        for (RouteDefinition route : routes) {
            dynamicRouteService.add(route);
        }
        informChanged();
        nacosListener(DATA_ID, nacosProperties.getGroup());
    }

    /**
     * Nacos 监听器
     *
     * @param dataId 路由名称
     * @param group 路由所属群组
     */
    public void nacosListener(String dataId, String group) {
        try {
            configService.addListener(dataId, group, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String s) {
                    log.info("网关更新:\n{} ", s);
                    List<RouteDefinition> definitions = JSON.parseArray(s, RouteDefinition.class);
                    for (RouteDefinition definition : definitions) {
                        dynamicRouteService.update(definition);
                    }
                }
            });
        } catch (NacosException e) {
            log.error("获取动态路由配置异常", e);
        }
    }

    /**
     * Nacos信息配置
     */
    private ConfigService initConfigService() {
        try {
            Properties properties = new Properties();
            properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());
            if (StringUtils.isNotBlank(nacosProperties.getNamespace())) {
                properties.setProperty(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());
            }
            if (StringUtils.isNotBlank(nacosProperties.getUsername())) {
                properties.setProperty(PropertyKeyConst.USERNAME, nacosProperties.getUsername());
            }
            if (StringUtils.isNotBlank(nacosProperties.getPassword())) {
                properties.setProperty(PropertyKeyConst.PASSWORD, nacosProperties.getPassword());
            }
            return NacosFactory.createConfigService(properties);
        } catch (Exception e) {
            log.error("初始化ConfigService异常", e);
            return null;
        }
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
}
